#include "../include/naryTree.h"
#include "../include/binsrchtree.h"
#include "../include/general_vfs_op.h"
#include "../include/header.h"
#include "../include/constants.h"
#include <stdio.h>
#include <string.h>
#include<stdlib.h>



/*
Function Name: add_file(...)
Descriptions: add a file to the vfs
Parameters: file path and name in vfs and the source data for file
Return Type: int: successful/unsuccessful and values
    corresponding to other errors.
*/
int add_file(char* path,char* filename,char* sourcefile)
{
#if OPER_DEBUG
    printf("IN ADD FILE\n");
#endif
    FILE * fptr; /*source file pointer*/
    int j,i,ret=10;
    extern  mainStr *mainstr;
    extern unsigned long **listarr;
    long int new_file_len;
    long offset;
    
    char f_name[CHARSIZE],f_type[TYPESIZE],*temp,*full_path;
    writeBlock *write;
    mainheader *chkhead;
    fdesc *assign_fd,*dest_fd;
    write=(writeBlock*)malloc(sizeof(writeBlock));
    assign_fd=(fdesc*)malloc(sizeof(fdesc));
    int valid=-1,pop_ret_val=-1;

    if(if_valid_args_(path))
    {
        if(if_valid_args_(filename))
        {
            if(if_valid_args_(sourcefile));
            else
                return 0;
        }
        else
            return 0;
    }
    else
        return 0;
    valid=validate_name(filename);
    if(valid==0)
        return 2;/*err2*/
    if(mainstr==NULL || mainstr->is_mounted!=5)
        return 7;
    else
    {
        remove_last_slash_(path);
        fptr = fopen(sourcefile, "rb"); /*open source file*/
        if(fptr == NULL)
        {
            return 5;/*err 7 source file not exist; */
        }
       dest_fd=bst_search_fd(datastr->bst,path);
        if(dest_fd==NULL)
            return 1; 
        else
        {
            fseek(fptr,0,2);
            new_file_len=ftell(fptr);
#if OPER_DEBUG
            printf("%ld len\n",new_file_len);
#endif
            if(new_file_len>BLOCKSIZE)
            {
                fclose(fptr);
                return 6;/*larger than blocksize err1 */
            }
            else
            {
                rewind(fptr);
                fread(write,sizeof(writeBlock),1,fptr);
#if OPER_DEBUG
                printf("data  %s\n",write->f_buffer);
                printf("lab:%s max:%d used:%d num_bl:%d list:%ld fDes:%ld fBl:%ld\n",
                       mainstr->vfs_read->f_label,
                       mainstr->vfs_read->max_fd,
                       mainstr->vfs_read->f_usedfDes,
                       mainstr->vfs_read->num_blocks,
                       mainstr->vfs_read->fList,
                       mainstr->vfs_read->fDes,
                       mainstr->vfs_read->fBlock);
#endif
                /*traverse through the array and find first empty location*/
                for(j = 0; j < mainstr->vfs_read->num_blocks; j++)
                {
                    if(listarr[j][1] == 0)
                    {
                        if(mainstr->vfs_read->f_usedfDes ==
                           mainstr->vfs_read->max_fd)
                            return 4; //file s/m full err err 4 in new ***
                        mainstr->vfs_read->f_usedfDes++;
                        strcpy(assign_fd->f_name,filename);
                        strcpy(assign_fd->f_path,path);
                        strcpy(assign_fd->f_type,"file");
                        assign_fd->f_size = new_file_len;
                        assign_fd->block_number = listarr[j][0];
                        listarr[j][1] = 1;
#if OPER_DEBUG
                        printf("mountpoint addr %p %ld\n",mainstr->mountpoint,
                               ftell(mainstr->mountpoint));
#endif
                        fseek(mainstr->mountpoint,listarr[j][0],0);
#if OPER_DEBUG
                        printf("ADDfd n:%s\tp:%s\tt:%s\t%ld\t%ld\n",
                               assign_fd->f_name,assign_fd->f_path,
                               assign_fd->f_type,assign_fd->f_size,
                               assign_fd->block_number);
                        printf("populate from addfile\n");
#endif
                        pop_ret_val=populate(datastr->htable,datastr->ntree,
                                             datastr->bst, assign_fd);
#if OPER_DEBUG
                        printf("populate ret to addfile\n");
#endif
                        if(pop_ret_val==0)
                        {
                            listarr[j][1] = 0;
                            mainstr->vfs_read->f_usedfDes--;
                            return 3;/*err3 exists */
                        }
                        else
                        {
                            fseek(mainstr->mountpoint, assign_fd->block_number,0);
                            fwrite(write,sizeof(writeBlock),1,mainstr->mountpoint);
                        }
#if OPER_DEBUG
                        printf("end of add success\n");
#endif

                        return ret;/*successful*/
                    }
                }
            }
        }
    }
    return -1;/*err 8 File error*/
}


/*
Function Name: Update_file(...)
Descriptions: update a file in vfs if it exists from an external file
Parameters: destination file in vfs, source file on disk
Return Type: int: successful/unsuccessful and values
    corresponding to other errors.
*/
int Update_file(char* dest,char* source)
{
    extern mainStr *mainstr;
    long offset;
    int ret=-1;
    char *file_dest,*file_src;
    extern dataStr *datastr;
    FILE *fptr;
    long int source_size;
    writeBlock *write;
    fdesc* dest_fd;
#if OPER_DEBUG
    bst_display(datastr->bst);
#endif
    if(if_valid_args_(dest))
    {
        if(if_valid_args_(source));
        else
            return 0;
    }
    else
        return 0;

    fptr = fopen(source, "r"); /*open source file*/
    if(fptr == NULL)
    {
        return 1;/*err 1 source file not exist*/
    }
    if(mainstr==NULL || mainstr->is_mounted!=5)
        return 6;
    dest_fd=bst_search_fd(datastr->bst,dest);
    if(dest_fd==NULL)
    {
        return 2;/*err 2 dest not found*/
    }
    else
    {
        write=(writeBlock*)malloc(sizeof(writeBlock));
        fseek(fptr,0,2);
        source_size=ftell(fptr);
#if OPER_DEBUG
        printf("size %ld\n",source_size);
#endif
        if(source_size>BLOCKSIZE)
        {
            return 4;/*src file exceeds size err...*/
        }
        rewind(fptr);
        fread(write,sizeof(writeBlock),1,fptr);
        fseek(mainstr->mountpoint,dest_fd->block_number,0);
        fwrite(write,sizeof(writeBlock),1,mainstr->mountpoint);
        dest_fd->f_size=source_size;
        return 10;/*success*/
    }
    return -1;/*error*/
}


/*
Function Name: remove_fdesc_data(...)
Descriptions: remove a FD data in data structures
Parameters: FD to be removed
Return Type: int: successful/unsuccessful
*/
int remove_fdesc_data(fdesc* fd)
{
    extern dataStr *datastr;
    int n_ret_val,h_ret_val,b_ret_val;
    n_ret_val=delete_node(&datastr->ntree,fd);
#if OPER_DEBUG
    printf("%d  n_ret_val\n",n_ret_val);
#endif
    if(n_ret_val==1)
    {
        b_ret_val=bst_remove(datastr->bst,fd);
        if(b_ret_val==1)
        {
            h_ret_val=delete_hashtable(datastr->htable,fd);
            if(h_ret_val==1)
                return 10;/*success*/
        }
    }
    return 0;/*unsuccessful*/
}


/*
Function Name: remove_file(...)
Descriptions: remove file from vfs data
Parameters: path of file to be removed
Return Type: int: successful/unsuccessful and values
    corresponding to other errors.
*/
int remove_file(char* path)
{
    int ret=-1,i,elim=-1;
    fdesc* elim_fd;
    extern dataStr *datastr;
    if(if_valid_args_(path));
    else
        return 0;
    if(mainstr==NULL || mainstr->is_mounted!=5)
        return 2;
    elim_fd=bst_search_fd(datastr->bst,path);
    if(elim_fd==NULL)
        return 1;
    for(i=0; i<mainstr->vfs_read->num_blocks; i++)
    {
        if(listarr[i][0]==elim_fd->block_number)
        {
            elim=i;
#if OPER_DEBUG
            printf("%d\n",elim);
#endif
            break;
        }
    }
#if OPER_DEBUG
    printf("-%s-%s-%s-\n",elim_fd->f_path,elim_fd->f_name,elim_fd->f_type);
#endif
    ret=remove_fdesc_data(elim_fd);
#if OPER_DEBUG
    printf("%d remove file return\n",ret);
#endif
    if(ret==10)
    {
        listarr[elim][1]=0;
        return 10;/*remove success*/
    }
    return 3;/*error*/
}


/*
Function Name: list_file(...)
Descriptions: write the contents of a text file in vfs to external disk.
    Search is done on BST.
Parameters: vfs text file path and destination on disk
Return Type: int: successful/unsuccessful and values
    corresponding to other errors.
*/
int list_file(char* file_path,char* output_file)
{
    FILE *fptr;/*output file pointer*/
    writeBlock *block;
    extern  mainStr *mainstr;
    extern  dataStr *datastr;
    fdesc *fd;
    char* temp,f_type[TYPESIZE];

    if(if_valid_args_(file_path))
    {
        if(if_valid_args_(output_file));
        else
            return 0;
    }
    else
        return 0;

    if(mainstr==NULL || mainstr->is_mounted!=5)
        return 6;/*not mounted*/
    fd=bst_search_fd(datastr->bst,file_path);
    if(fd==NULL)
        return 1;/*src not found*/

    temp=strrchr(file_path,'.');
      
    if(temp==NULL)
        strcpy(f_type,"");
    else
    strncpy(f_type,temp+1,TYPESIZE);

    if(strcmp(f_type,"txt")!=0)
        return 5;/*not text*/

    fptr=fopen(output_file,"w+");
    if(fptr==NULL)
        return 2;/*create destination error*/
#if OPER_DEBUG
    printf("%s %s %s %ld\n",fd->f_name,fd->f_path,fd->f_type,fd->block_number);
    printf("%ld\n",ftell(mainstr->mountpoint));
#endif
    block=(writeBlock*)malloc(sizeof(writeBlock));
    fseek(mainstr->mountpoint,fd->block_number,0);
    fread(block,sizeof(writeBlock),1,mainstr->mountpoint);
#if OPER_DEBUG
    printf("LIST DATA:\n %s\n",block->f_buffer);
#endif
    fwrite(block,sizeof(writeBlock),1,fptr);
    fclose(fptr);

    return 10;/*successful*/
}

/*
Function Name: export_file(...)
Descriptions: export a file from vfs to external disk.
    Search is done on BST.
Parameters: vfs file path and external path
Return Type: int: successful/unsuccessful and values
    corresponding to other errors.
*/

int export_file(char* file_path,char* output_file)
{
    FILE *fptr;/*output file pointer*/
    writeBlock *block;
    extern  mainStr *mainstr;
    extern  dataStr *datastr;
    fdesc *fd;

    if(if_valid_args_(file_path))
    {
        if(if_valid_args_(output_file));
        else
            return 0;
    }
    else
        return 0;
    if(mainstr==NULL || mainstr->is_mounted!=5)
        return 7;/*not mounted*/
#if OPER_DEBUG
    bst_display(datastr->bst);
    printf("%s %s %s %ld\n",datastr->bst->fileDes->f_name,
           datastr->bst->fileDes->f_path, datastr->bst->fileDes->f_type,
           datastr->bst->fileDes->block_number);
#endif
    fd=bst_search_fd(datastr->bst,file_path);
    if(fd==NULL)
        return 1;/*src not found*/
    if(strcmp(fd->f_type,"DIR")==0)
        return 6;
    fptr=fopen(output_file,"w+");
    if(fptr==NULL)
        return 2;

    block=(writeBlock*)malloc(sizeof(writeBlock));
    fseek(mainstr->mountpoint,fd->block_number,0);
    fread(block,sizeof(writeBlock),1,mainstr->mountpoint);
    fwrite(block,sizeof(writeBlock),1,fptr);
    fclose(fptr);

    return 10;
}


/*
Function Name: copy_file(...)
Descriptions: copy a file from vfs to another location on vfs
Parameters: vfs file path and destination path in vfs
Return Type: int: successful/unsuccessful and values
    corresponding to other errors.
*/

int copy_file(char* P1,char* P2)
{
    writeBlock *block;
    extern unsigned long **listarr;
    extern  mainStr *mainstr;
    extern  dataStr *datastr;
    char *filename,*ftype,*fpath;
    int i=0,j=0,pop_ret_val;
    filename=(char*)malloc(sizeof(char)*CHARSIZE);
    ftype=(char*)malloc(sizeof(char)*CHARSIZE);
    fpath=(char*)malloc(sizeof(char)*CHARSIZE);
    fdesc *fd_src_file,*fd_dst_file,*fd_dst_path;
    if(if_valid_args_(P1))
    {
        if(if_valid_args_(P2));

        else
            return 0;
    }
    else
        return 0;
     if(validate_name(P2))
        return 4;

    filename=strrchr(P2,'/');
    i=strlen(P2);
    j=strlen(filename);
    strncpy(fpath,P2,(i-j+1));
    fpath[i-j+1]='\0';
    ftype=strrchr(P2,'.');
#if OPER_DEBUG
    printf("n:%s p:%s t:%s\n",filename+1,fpath,ftype+1);
    bst_display(datastr->bst);
#endif
    if(mainstr==NULL || mainstr->is_mounted !=5)
        return 7;/*not mounted . using unmount err*/

    fd_src_file=bst_search_fd(datastr->bst,P1);

    if(fd_src_file==NULL)
        return 1;/*src not found*/
#if OPER_DEBUG
    printf(" searched: -%s-%s-%s-\n",fd_src_file->f_name,fd_src_file->f_path,fd_src_file->f_type);
#endif
    if(strcmp(fd_src_file->f_type,"DIR")==0)
        return 5;/*src dir error*/
    /*to check the whether the free blocks*/
    for(j = 0; j < mainstr->vfs_read->num_blocks; j++)
    {
        if(listarr[j][1] == 0)
        {
            if(mainstr->vfs_read->f_usedfDes == mainstr->vfs_read->max_fd)
                return 6;/*full*/
#if OPER_DEBUG
            bst_display(datastr->bst);
#endif
            fd_dst_file=bst_search_fd(datastr->bst,P2);
            if(fd_dst_file==NULL)
            {
                fd_dst_file=(fdesc*)malloc(sizeof(fdesc));
                mainstr->vfs_read->f_usedfDes++;
                strcpy(fd_dst_file->f_name,filename+1);
                strcpy(fd_dst_file->f_path,fpath);
                if(ftype!=NULL)
                 strcpy(fd_dst_file->f_type,ftype+1);
                else
                 strcpy(fd_dst_file->f_type,"");
                fd_dst_file->f_size =fd_src_file->f_size;
                fd_dst_file->block_number = listarr[j][0];

                listarr[j][1] = 1;
                fseek(mainstr->mountpoint,listarr[j][0],0);
                pop_ret_val=populate(datastr->htable,datastr->ntree,datastr->bst, fd_dst_file);
            }

            block=(writeBlock*)malloc(sizeof(writeBlock));
            fseek(mainstr->mountpoint,fd_src_file->block_number,0);
            fread(block,sizeof(writeBlock),1,mainstr->mountpoint);
            fseek(mainstr->mountpoint, fd_dst_file->block_number,0);
            fwrite(block,sizeof(writeBlock),1,mainstr->mountpoint);
            return 10;/*successful*/
        }
    }
    return 9;/*error*/
}

/*
Function Name: search_File(...)
Descriptions: search file in vfs using Hashtable and write all files having the
     given 'name' as prefix.
Parameters: file name and external path on disk
Return Type: int: successful/unsuccessful and values
    corresponding to other errors.
*/


int search_File(char *name, char *outputfile)
{
    l_node *start;
    l_node *curr;
    if(if_valid_args_(name))
        if(if_valid_args_(outputfile));
        else return 0;
    else return 0;

    if(mainstr==NULL || mainstr->is_mounted!=5)
        return 1;

    start = search_pre_hashtable(datastr->htable, name);
    curr=start;
    FILE *ft = fopen (outputfile, "w+" ) ;
    if(ft!=NULL)
    {
        while(curr != NULL)
        {
            fprintf(ft, "%s\t%s\n", curr->data->f_name, curr->data->f_path) ;
            curr=curr->next;
        }
        fclose(ft);
    }
    return 10;
}

/*
Function Name: move_file(...)
Descriptions: move a file from vfs to another location in vfs
Parameters: vfs source file path and vfs destination file path
Return Type: int: successful/unsuccessful and values
    corresponding to other errors.
*/


int move_file(char *P1,char *P2)
{
    fdesc *src_fd,*dest_path_fd,*dest_file_fd,*f_fd;
    int res=-1;
    char *dest_file;
    char *dest_path;
    int len_temp,len_path;
    dest_path=(char*)malloc(sizeof(char)*CHARSIZE);

    if(if_valid_args_(P1))
        if(if_valid_args_(P2));
        else return 0;
    else return 0;
    if(mainstr==NULL || mainstr->is_mounted!=5)
        return 6;/*not mounted*/
    src_fd=bst_search_fd(datastr->bst,P1);
    if(src_fd==NULL)
        return 1;/*not found src*/
    if(src_fd!=NULL && strcmp(src_fd->f_type,"DIR")==0)
        return 1;/*src is DIR*/
    remove_last_slash_(P2);
    dest_path_fd=bst_search_fd(datastr->bst,P2);
    if(dest_path_fd==NULL)
        return 2;
    if(dest_path_fd!=NULL && strcmp(dest_path_fd->f_type,"DIR")!=0)
        return 2;
       
    strcpy(dest_path,P2);
    strcat(dest_path,strrchr(P1,'/'));
    
    f_fd=bst_search_fd(datastr->bst,dest_path);    
    
    if(f_fd!=NULL && strcmp(f_fd->f_type,"file")==0)
        return Update_file(dest_path,P1);

    if(f_fd==NULL){
        return 10;
        res=move_Function(datastr->ntree,P1,P2,"file");
       }
    if(res==10)
    {
        /* bst_display(datastr->bst);
        if(bst_remove(datastr->bst,src_fd))
        {
            printf("here");
            if(delete_hashtable(datastr->htable,src_fd));
            else
            {
                printf("there");
                bst_insert(datastr->bst,src_fd);
                res=insert_node(&(datastr->ntree),src_fd);
                return 7;
            }
        }
        else
        {
            res=insert_node(&(datastr->ntree),src_fd);
            return 7;
        }

    }
    return res;*/
        
        datastr->bst=NULL;
        //datastr->bst=(b_node *)malloc(sizeof(b_node));
        //datastr->bst->key=(char*)malloc(sizeof(char)*CHARSIZE);
         
        if(init_bst(datastr->bst,datastr->ntree->data))
        {
            
            if(bst_recursive_insert(datastr->bst,datastr->ntree->child))
            {
                datastr->htable=NULL;
                //datastr->htable=(hashtable *)malloc(sizeof(hashtable));
                initialize_hashtable(datastr->htable);
                if(ht_recursive_insert(datastr->htable,datastr->ntree->child))
                    return 10;
                else
                    return 7;
                return 10;
            }
            else return 7;
        }
        else return 7;
    }
    return res;
}

